#ifndef __DECODE_H
#define __DECODE_H

#include "../utils/error.h"
#include "./base.h"

namespace serialize {

template <typename T> inline T decode_int(const uint8_t *buf) {
    static_assert(is_int<T>, "invalid type");
    if constexpr (is_8bit<T>) {
        return static_cast<T>(buf[0]);
    } else if constexpr (is_16bit<T>) {
        return (static_cast<T>(buf[0]) << 8) + static_cast<T>(buf[1]);
    } else if constexpr (is_32bit<T>) {
        return (static_cast<T>(buf[0]) << 24) + (static_cast<T>(buf[1]) << 16) +
               (static_cast<T>(buf[2]) << 8) + static_cast<T>(buf[3]);
    }
}

template <typename T, typename... Ts> constexpr bool is_all_fixed() {
    using TYPE = std::decay_t<T>;
    if constexpr (sizeof...(Ts) == 0) {
        return is_int<TYPE>;
    } else {
        return is_int<TYPE> && is_all_fixed<Ts...>();
    }
}

template <typename T, typename... Ts> constexpr uint8_t all_fixed_size() {
    using TYPE = std::decay_t<T>;
    if constexpr (sizeof...(Ts) == 0) {
        return sizeof(TYPE);
    } else {
        return sizeof(TYPE) + all_fixed_size<Ts...>();
    }
}

template <typename T, typename... Ts>
void decode_fixed_all(const uint8_t *buf, T &value, Ts &...ts) {
    using TYPE = std::decay_t<T>;
    value = decode_int<TYPE>(buf);
    if constexpr (sizeof...(Ts) != 0) {
        decode_fixed_all(buf + sizeof(TYPE), ts...);
    }
}

template <typename T, typename... Ts>
utils::Error decode_all(const uint8_t *buf, uint8_t len, T &value, Ts &...ts) {
    using TYPE = std::decay_t<T>;
    static_assert(is_int<TYPE> || is_byte_view<TYPE>, "INVALID TYPE");

    if constexpr (is_all_fixed<T, Ts...>()) {
        constexpr uint8_t LEN = all_fixed_size<T, Ts...>();
        if (LEN > len) {
            return utils::ec::PARSE_ERR;
        }
        decode_fixed_all(buf, value, ts...);
		return utils::ec::OK;
    } else {
        if constexpr (is_int<TYPE>) {
            constexpr uint8_t LEN = sizeof(TYPE);
            if (LEN > len) {
                return utils::ec::PARSE_ERR;
            }
            value = decode_int<TYPE>(buf);
            if constexpr (sizeof...(Ts) != 0) {
                return decode_all(buf + LEN, len - LEN, ts...);
            } else {
				return utils::ec::OK;
			}
        } else if constexpr (is_byte_view<TYPE>) {
            if (len < 1) {
                return utils::ec::PARSE_ERR;
            }
            uint8_t LEN = 1 + buf[0];
            if (LEN > len) {
                return utils::ec::PARSE_ERR;
            }
            value.buf = buf + 1;
            value.len = buf[0];
            if constexpr (sizeof...(Ts) != 0) {
                return decode_all(buf + LEN, len - LEN, ts...);
            } else {
				return utils::ec::OK;
			}
        }
    }
}

} // namespace serialize

#endif
